/*
 * Copyright Radosław Smogura, 2012. All rights reserved.
 *
 * The content of this file is confidential and it is intelectual property
 * of copyright owners.
 * 
 *
 * $URL: http://reprap-ide.googlecode.com/svn/trunk/RepRapIDE/RepRapProject/src/main/java/eu/softper/reprap/generator/SceneHandler.java $
 * $Id: SceneHandler.java 3 2012-11-19 10:58:30Z mail@smogura.eu $
 * $Revision: 3 $
 */
package eu.softper.reprap.generator;

import eu.smogura.stlparser.STLParserAbstractHandler;
import eu.softper.reprap.utils.BoundMinMaxCollector;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedList;

/**
 *
 * @author rsmogura
 */
public class SceneHandler<T extends Number & Comparable<T>> extends STLParserAbstractHandler {
    private static final BigDecimal HALF = new BigDecimal("0.5");
    private static final DecimalVertex.ZIndexComparator zIndexComparotr = new DecimalVertex.ZIndexComparator();
    
    private LinkedList<SceneTriangle<T>> triangles = new LinkedList<SceneTriangle<T>>();
            
    private GenerationContextImpl ctx;

    private float[][] vTmp = new float[3][];
    private float[][] orderedVerts = new float[3][];
    private static ArrayVertexZCompartor arrayZComparator = new ArrayVertexZCompartor();
    
    private BoundMinMaxCollector<T> minMax;
    
    private BaseCache<DecimalVertex> verticesCache;
    private DecimalVertex facetNormal;
    
    private NumericFactory<T> nf;
    
    private void rollArray(float[][] vTmp, int pos) {
        orderedVerts[0] = vTmp[(0 + pos) % vTmp.length];
        orderedVerts[1] = vTmp[(1 + pos) % vTmp.length];
        orderedVerts[2] = vTmp[(2 + pos) % vTmp.length];
    }
    
    public SceneHandler(NumericFactory<T> nf, GenerationContextImpl ctx) {
        minMax = new BoundMinMaxCollector<T>(nf);
        this.ctx = ctx;
        this.verticesCache = ctx.getGeneratorCache().getVerticesCache();
        this.nf = nf;
    }
    
    
    protected T condRound(T in, BigDecimal step) {
        if (step == null || BigDecimal.ZERO.compareTo(step) == 0)
            return in;
        return nf.round(in, nf.valueOf(step));        
    }
    
    @Override
    public void beginFacet(float[] normal) {
        super.beginFacet(normal);
        facetNormal = verticesCache.intern(new DecimalVertex(
                condRound(nf.valueOf(normal[0]), ctx.getStepX()),
                condRound(nf.valueOf(normal[1]), ctx.getStepY()),
                condRound(nf.valueOf(normal[2]), ctx.getStepZ())));
    }

    @Override
    public void endFacet() {
        super.endFacet();
        facetNormal = null;
    }
    
    @Override
    public void beginTriangle(float[] v1, float[] v2, float[] v3) {
        super.beginTriangle(v1, v2, v3);

        DecimalVertex[] verts = new DecimalVertex[3];
        vTmp[0] = v1;
        vTmp[1] = v2;
        vTmp[2] = v3;
        
        /* Vertices may be only rolled. */
        if (v1[2] < v2[2]) {
            if (v1[2] < v3[2]) {
                rollArray(vTmp, 0);
            }else {
                /* v3 is minimum */
                rollArray(vTmp, 2);
            }
        }else {
            /* v2 may be first. */
            if (v2[2] < v3[2]) {
                rollArray(vTmp, 1);
            }else {
                /* v3 is minimum */
                rollArray(vTmp, 2);
            }
        }

        verts[0] = verticesCache.intern(new DecimalVertex(
                condRound(nf.mul(nf.add(nf.valueOf(orderedVerts[0][0]),nf.valueOf(ctx.getOffsetX())),nf.valueOf(ctx.getScaleX())), ctx.getStepX()),
                condRound(nf.mul(nf.add(nf.valueOf(orderedVerts[0][1]),nf.valueOf(ctx.getOffsetY())),nf.valueOf(ctx.getScaleY())), ctx.getStepY()),
                condRound(nf.mul(nf.add(nf.valueOf(orderedVerts[0][2]),nf.valueOf(ctx.getOffsetZ())),nf.valueOf(ctx.getScaleZ())), ctx.getStepZ())));
        
        verts[1] = verticesCache.intern(new DecimalVertex(
                condRound(nf.mul(nf.add(nf.valueOf(orderedVerts[1][0]),nf.valueOf(ctx.getOffsetX())),nf.valueOf(ctx.getScaleX())), ctx.getStepX()),
                condRound(nf.mul(nf.add(nf.valueOf(orderedVerts[1][1]),nf.valueOf(ctx.getOffsetY())),nf.valueOf(ctx.getScaleY())), ctx.getStepY()),
                condRound(nf.mul(nf.add(nf.valueOf(orderedVerts[1][2]),nf.valueOf(ctx.getOffsetZ())),nf.valueOf(ctx.getScaleZ())), ctx.getStepZ())));
        
        verts[2] = verticesCache.intern(new DecimalVertex(
                condRound(nf.mul(nf.add(nf.valueOf(orderedVerts[2][0]),nf.valueOf(ctx.getOffsetX())),nf.valueOf(ctx.getScaleX())), ctx.getStepX()),
                condRound(nf.mul(nf.add(nf.valueOf(orderedVerts[2][1]),nf.valueOf(ctx.getOffsetY())),nf.valueOf(ctx.getScaleY())), ctx.getStepY()),
                condRound(nf.mul(nf.add(nf.valueOf(orderedVerts[2][2]),nf.valueOf(ctx.getOffsetZ())),nf.valueOf(ctx.getScaleZ())), ctx.getStepZ())));

        SceneTriangle triangle = new SceneTriangle(verts[0], verts[1], verts[2], facetNormal);
        triangles.add(triangle);
        minMax.processTriangle(triangle);
    }

    public LinkedList<SceneTriangle<T>> getTriangles() {
        return triangles;
    }

    public T getMinZ() {
        return minMax.getMinZ();
    }

    public T getMinY() {
        return minMax.getMinY();
    }

    public T getMinX() {
        return minMax.getMinX();
    }

    public T getMaxZ() {
        return minMax.getMaxZ();
    }

    public T getMaxY() {
        return minMax.getMaxY();
    }

    public T getMaxX() {
        return minMax.getMaxX();
    }
}
